/* eslint-disable */

KISSY.add( 'modelproxy', function ( S, IO ) {
  function Proxy( options ) {
    this._opt = options;
  }
  Proxy.prototype = {
    request: function( params, callback, errCallback ) {
      IO( {
        url: Proxy.base + '/' + this._opt.id,
        data: params,
        type: this._opt.method,
        dataType: this._opt.dataType,
        success: callback,
        error: errCallback
      } );
    },
    getOptions: function() {
      return this._opt;
    }
  };

  Proxy.objects = {};

  Proxy.create = function( id ) {
    if ( this.objects[ id ] ) {
      return this.objects[ id ];
    }
    var options = this._interfaces[ id ];
    if ( !options ) {
      throw new Error( 'No such interface id defined: '
        + id + ', please check your interface configuration file' );
    }
    return this.objects[ id ] = new this( options );
  },

    Proxy.configBase = function( base ) {
      if ( this.base ) return;
      this.base = ( base || '' ).replace( /\/$/, '' );
      var self = this;
      // load interfaces definition.
      IO( {
        url: this.base + '/$interfaces',
        async: false,
        type: 'get',
        dataType: 'json',
        success: function( interfaces ) {
          self.config( interfaces );
        },
        error: function( err ) {
          throw err;
        }
      } );
    };

  Proxy.config = function( interfaces ) {
    this._interfaces = interfaces;
  };

  Proxy.getInterfaceIdsByPrefix = function( pattern ) {
    if ( !pattern ) return [];
    var ids = [], map = this._interfaces, len = pattern.length;
    for ( var id in map ) {
      if ( id.slice( 0, len ) == pattern ) {
        ids.push( id );
      }
    }
    return ids;
  };

  function ModelProxy( profile ) {
    if ( !profile ) return;

    if ( typeof profile === 'string' ) {
      if ( /^(\w+\.)+\*$/.test( profile ) ) {
        profile = Proxy
          .getInterfaceIdsByPrefix( profile.replace( /\*$/, '' ) );

      } else {
        profile = [ profile ];
      }
    }
    if ( profile instanceof Array ) {
      var prof = {}, methodName;
      for ( var i = profile.length - 1; i >= 0; i-- ) {
        methodName = profile[ i ];
        methodName = methodName
          .substring( methodName.lastIndexOf( '.' ) + 1 );
        if ( !prof[ methodName ] ) {
          prof[ methodName ] = profile[ i ];

        } else {
          methodName = profile[ i ].replace( /\./g, '_' );
          prof[ methodName ] = profile[ i ];
        }
      }
      profile = prof;
    }

    for ( var method in profile ) {
      this[ method ] = ( function( methodName, interfaceId ) {
        var proxy = Proxy.create( interfaceId );
        return function( params ) {
          params = params || {};
          if ( !this._queue ) {
            this._queue = [];
          }
          this._queue.push( {
            params: params,
            proxy: proxy
          } );
          return this;
        };
      } )( method, profile[ method ] );
    }
  }

  ModelProxy.prototype = {
    done: function( f, ef ) {
      if ( typeof f !== 'function' ) return;

      if ( !this._queue ) {
        f.apply( this );
        return;
      }
      this._sendRequestsParallel( this._queue, f, ef );

      this._queue = null;
      return this;
    },
    _sendRequestsParallel: function( queue, callback, errCallback ) {
      var args = [], self = this;

      var cnt = queue.length;

      for ( var i = 0; i < queue.length; i++ ) {
        ( function( reqObj, k ) {
          reqObj.proxy.request( reqObj.params, function( data ) {
            args[ k ] = data;
            --cnt || callback.apply( self, args );
          }, function( err ) {
            errCallback = errCallback || self._errCallback;
            if ( typeof errCallback === 'function' ) {
              errCallback( err );

            } else {
              console.error( 'Error occured when sending request ='
                , reqObj.proxy.getOptions(), '\nCaused by:\n', err );
            }
          } );
        } )( queue[i], i );
      }
    },
    error: function( f ) {
      this._errCallback = f;
    }
  };

  ModelProxy.create = function( profile ) {
    return new this( profile );
  };

  ModelProxy.configBase = function( path ) {
    Proxy.configBase( path );
  };

  return ModelProxy;

}, { requires: ['io'] } );
